package MultiSnake;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class Server extends UnicastRemoteObject implements ServerInterface {
	/*
	 * how many players
	 */
	private int playersOfNum = 2;

	/*
	 * food position
	 */
	static int x = 0, y = 0;

	/*
	 * Snake start positions
	 */
	private static final int[][] snakePositionsX = {{2,0,0,0},{0,58,0,0},{0,0,2,0},{0,0,0,58}}; 
	private static final int[][] snakePositionsY = {{2,0,0,0},{0,2,0,0},{0, 0, 58, 0},{0,0,0,58}}; 
	/*
	 * Game board and snake width and height
	 */
	public static final int Width = 600;
	public static final int Height = 600;
	public static final int nodeWidth = 10;
	public static final int nodeHeight = 10;
	public static int fx;
	public static int fy;

	int maxX = Width / nodeWidth;
	int maxY = Height / nodeHeight;

//	ServerSocket ss;
//	Socket s;
//	ObjectInputStream ois;
//	ObjectOutputStream oos;
	private List<ClientInterface> clients = new LinkedList<ClientInterface>();
	int num = 0;
	// String connect = "";
	Thread[] thread;
	MultiSnakeModel[] list;
	int[] score;
	boolean[][] isFood;
	boolean[] running;

	public Server(int playersOfNum) throws RemoteException {
		this.playersOfNum = playersOfNum;
		thread = new Thread[playersOfNum];
		list = new MultiSnakeModel[playersOfNum];
		score = new int[playersOfNum];
		running = new boolean[playersOfNum];
		
		/*
		 * Create the Registry. At the moment, I am simply creating it with no
		 * checks. With some checks, I can connect to the registry if a registry
		 * already exists
		 */
		Registry reg;
		try {
			reg = LocateRegistry.createRegistry(12345);

			/* Bind the Server to the RMI Registry */
			reg.rebind("Server", this);

		} catch (RemoteException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		System.out.println("Server Bound to RMIRegistry");

		// for (int i = 0; i < running.length; i++) {
		// running[i] = true;
		// }

		isFood = new boolean[maxX][];
		for (int i = 0; i < maxX; ++i) {
			isFood[i] = new boolean[maxY];
			Arrays.fill(isFood[i], false);
		}
		// ss = new ServerSocket(5555);
		// while (num < playersOfNum) {
		System.out.println("Wait to connect...");
		// s = ss.accept();
		// System.out.println(s.getInetAddress() + ":" + s.getPort() +
		// "--- player" + (num+1));
		// oos = new ObjectOutputStream(s.getOutputStream());
		// oos.writeObject(Width + "");
		// oos.writeObject(Height + "");
		// oos.writeObject(nodeWidth + "");
		// oos.writeObject(nodeHeight + "");
		// list[num] = new LinkedList();
		// thread[num] = new ServerThread(s, num);
		if (num == 0) {
			Random r = new Random();
			fx = r.nextInt(maxX);
			fy = r.nextInt(maxY);
			// isFood[x][y] = true;
			this.setFood(fx, fy);
		}
		num++;
		// }
		// for (int i = 0; i < playersOfNum; i++) {
		// thread[i].start();
		// }
	}

	public static void main(String[] args) {
		int playersOfNum = 0;
		Scanner keyboard = new Scanner(System.in);
		do {
			System.out.print("\nEnter number of players (2-4):");
			try {
				playersOfNum = Integer.parseInt(keyboard.next());
			} catch (Exception e) {
				// Do nothing
				playersOfNum = 0;
			}
		} while (playersOfNum < 2 || playersOfNum > 4);
		try {
			new Server(playersOfNum);
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	class ServerThread extends Thread {
		//
		// ObjectInputStream ois;
		// ObjectOutputStream oos;
		// Socket s;
		// int num;
		//
		boolean[][] isSnake;
		ClientInterface client;

		/**
		 * Initialise snake for each player
		 * 
		 * @param num
		 */
		public ServerThread(int num, ClientInterface client) {
			this.client = client;
			//list[num].addLast(new Node(10 * num, 10 * num));
//			isSnake[10 * num][10 * num] = true;
			// this.s = s;
			// this.num = num;
			//
		}

		//
		public void run() {
			while (true) {
				// call the client repaint function from here
				// ---------------------------------------
				//this.client.repaint(list);

				// try {
				// ois = new ObjectInputStream(s.getInputStream());
				// String str = (String) ois.readObject();
				// if (str.equals("update")) {
				// list[num] = (LinkedList) ois.readObject();
				// running[num] = (Boolean) ois.readObject();
				// oos = new ObjectOutputStream(s.getOutputStream());
				// if (num == 0) {
				// oos.writeObject(list[1]);
				// oos.writeObject(score[1]);
				// oos.writeObject(running[1]);
				// }
				// if (num == 1) {
				// oos.writeObject(list[0]);
				// oos.writeObject(score[0]);
				// oos.writeObject(running[0]);
				// }
				//
				// oos.writeObject(x);
				// oos.writeObject(y);
				// oos.writeObject(isFood);
				// } else if (str.equals("food")) {
				// score[num] = Integer.parseInt(ois.readObject()
				// .toString());
				// for (int i = 0; i < maxX; ++i) {
				// isFood[i] = new boolean[maxY];
				// Arrays.fill(isFood[i], false);
				// }
				// oos = new ObjectOutputStream(s.getOutputStream());
				// Random r = new Random();
				// x = r.nextInt(maxX);
				// y = r.nextInt(maxY);
				// isFood[x][y] = true;
				// oos.writeObject(x);
				// oos.writeObject(y);
				// oos.writeObject(isFood);
				// } else if (str.equals("start")) {
				// oos = new ObjectOutputStream(s.getOutputStream());
				// oos.writeObject(x + "");
				// oos.writeObject(y + "");
				// oos.writeObject(isFood);
				// } else if (str.equals("stop")) {
				// running[0] = false;
				// running[1] = false;
				// }
				// } catch (Exception e) {
				// System.out.println("player" + (num + 1) + " left");
				// break;
				// }
			}
		}
	}

	/**
	 * When a client/player is registered with the server instance, the created
	 * client interface is passed in.
	 * 
	 * @param ClientInterface
	 */
	public void register(ClientInterface client) throws RemoteException {
		// TODO Auto-generated method stub
		System.out.println("Server has registered new Client");

		synchronized (clients) {
			/* Remember, all RMI calls should be assumed to be separate threads. */
			int index = clients.size();
			System.out.println("index="+index);
			// Set the index of the client
			client.setIndex(index);
			System.out.println("client.getIndex()="+client.getIndex());
			
			MultiSnakeModel snakeModel = new MultiSnakeModel(this, client.getSelf() , snakePositionsX[index][index], snakePositionsY[index][index]);
			if(index==1){
				snakeModel.oppNodeArray = list[0].myNodeArray;
				list[0].oppNodeArray = snakeModel.myNodeArray;
			}
			client.setModel(snakeModel);
			list[index] = snakeModel;
			clients.add(client);
			
			//thread[index] = new ServerThread(index, client);
		}

		(new Thread() {
			public void run() {
				synchronized (clients) {
					/*
					 * If ever dealing with a shared resource, ESPECIALLY
					 * iteration you MUST do this. Fail to do this at your
					 * peril.
					 */
					for (ClientInterface client : clients) {
						try {
							/*
							 * What happens if one of the clients disconnects?
							 * You will likely get a ConnectException error. At
							 * the moment it is easy to handle just this one
							 * call... but what if your Client Interface is too
							 * complex? You certainly want to have a single
							 * class that will handle all RMI calls.
							 */
							client.alertNewUser("Client " + clients.size());
						} catch (RemoteException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}).start();		
	}

	public void startThreads() throws RemoteException{
		System.out.println("startThreads");
		System.out.println("------------this.playersOfNum = "+this.playersOfNum+" == clients.size()="+clients.size());
		if (this.playersOfNum == clients.size()) {
			for (ClientInterface client1 : clients) {
				System.out.println(client1.getIndex());
				client1.begin(this);
			}

			// this thread will write the food items to clients
			(new Thread() {
				public void run() {
					while (true) {
						Random r = new Random();
						x = r.nextInt(maxX);
						y = r.nextInt(maxY);
						isFood[x][y] = true;
						for (ClientInterface client : clients) {
							/**
							 * call the food function in the client and send the random food item
							 * same food position will be send to all clients
							 */
							try {
								client.addFood(isFood);
							} catch (RemoteException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
				}
			}).start();
		}
	}
	/*
	 * Get client width
	 * 
	 * @see MultiSnake.ServerInterface#getWidth()
	 */
	public int getWidth() throws RemoteException {
		return Server.Width;
	}

	/*
	 * Get client height
	 * 
	 * @see MultiSnake.ServerInterface#getHeight()
	 */
	public int getHeight() throws RemoteException {
		return Server.Height;
	}

	/*
	 * Get client node width
	 * 
	 * @see MultiSnake.ServerInterface#getNodeWidth()
	 */
	public int getNodeWidth() throws RemoteException {
		return Server.nodeWidth;
	}

	/*
	 * Get client node height
	 * 
	 * @see MultiSnake.ServerInterface#getNodeHeight()
	 */
	public int getNodeHeight() throws RemoteException {
		return Server.nodeHeight;
	}

	@Override
	public void setFood(int x, int y) throws RemoteException {
		// TODO Auto-generated method stub
		isFood[x][y] = true;
	}

	@Override
	public boolean getFood() throws RemoteException {
		// TODO Auto-generated method stub
		return isFood[fx][fy];
	}

	@Override
	public MultiSnakeModel[] getSnakes() throws RemoteException {
		return this.list;
	}
}